Python 学习笔记基础
基础
- 语法
#
开头,注释- 缩进的语句视为代码块
- 大小写敏感
数据类型和变量
True
和False
首字母大写and
、or
和not
- 空值:
None
字符串格式化
%
%d
整数%f
浮点数%s
字符串%x
十六进制整数补位
1
2
3
4'%2d-%02d' % (3, 1)
' 3-01'
'%.2f' % 3.1415926
'3.14'
数组:
list
和tuple
list
数组len(list)
得到长度list[-2]
获得倒数第二个元素list.append(ele)
往 list 中追加元素到末尾list.insert(1, ele)
,把元素插入到指定的位置,比如索引号为1
的位置list.pop()
,删除 list 末尾的元素,用pop()
方法list.pop(i)
删除指定位置的元素,用pop(i)
方法,其中i
是索引位置- 元素的数据类型也可以不同,
L = ['Apple', 123, True]
tuple
数组:classmates = ('Michael', 'Bob', 'Tracy')
tuple
一旦初始化就不能修改,代码更安全
条件判断和循环
if
elif
else
for in
while
range()
函数1
2
3
4
5
61,5) #代表从1到5(不包含5) range(
[1, 2, 3, 4]
1,5,2) #代表从1到5,间隔2(不包含5) range(
[1, 3]
5) #代表从0到5(不包含5) range(
[0, 1, 2, 3, 4]raw_inpit(str)
读取的内容永远以字符串的形式返回
dict
和set
dict
就是map
,用 key-value 的形式存储。1
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
根据
key
获得value
[]
:一旦key
不存在就会报错get()
函数:如果 key 不存在,可以返回 None,或者自己指定的 value(作为第二个参数传入)
set
是一组 key 的集合,但不存储 value,key 不能重复。- 需要 list 作为输入
add(key)
函数用来往里面添加元素,自动忽略重复remove(key)
函数用来删除元素&
操作用来做交集|
操作用来做并集
函数
Python 内置数据类型转换函数:如
int()
Python 可以给函数赋别名,如:
a = abs
定义函数,
def
:1
2
3
4
5def my_abs(x):
if x >= 0:
return x
else:
return -x如果没有
return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
。空函数,
pass
:1
2def nop():
pass类型检查,
isinstance
:1
2
3
4
5
6
7def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -xPython 函数可以返回多个值:
1
2
3
4
5
6import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny1
2
3100, 100, 60, math.pi / 6) x, y = move(
print x, y
151.961524227 70.0事实上,它并没有返回多个值,而是返回了一个 tuple
Python 具有默认参数:
1
2
3
4
5
6def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s默认参数必须指向不变对象,不然默认参数就会不断变化,例子:
1
2
3def add_end(L=[]):
L.append('END')
return L1
2
3
4
5
6add_end()
['END']
add_end()
['END', 'END']
add_end()
['END', 'END', 'END']可变参数,
*
:1
2
3
4
5def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum1
2
3
41, 2) calc(
5
calc()
0在函数内部,参数
numbers
接收到的是一个 tuple。如果已经有一个 list 或者 tuple,可以这么写:
1
2
31, 2, 3] nums = [
calc(*nums)
14关键字参数:
**
,会把多与参数自动组装成一个dict
:1
2def person(name, age, **kw):
print 'name:', name, 'age:', age, 'other:', kw1
2
3
4
5
6'Michael', 30) person(
name: Michael age: 30 other: {}
'Bob', 35, city='Beijing') person(
name: Bob age: 35 other: {'city': 'Beijing'}
'Adam', 45, gender='M', job='Engineer') person(
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}1
2
3'city': 'Beijing', 'job': 'Engineer'} kw = {
'Jack', 24, **kw) person(
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数
高级特性
切片(Slice )
1 | 'Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] L = [ |
L[0:3]
表示,从索引 0 开始取,直到索引 3 为止,但不包括索引 3。即索引 0,1,2,正好是 3 个元素。- 如果第一个索引是 0,还可以省略。
L[-1]
取倒数第一个元素,也支持倒数切片:L(-2:)
- 只写
[:]
就可以原样复制一个 list L[:10:2]
表示前十个元素,每两个取一个:[0,2,4,6,8]
- tuple 也可以用切片,操作结果也是 tuple
- 字符串也支持切片
迭代(Iteration)
只要是可迭代对象(list,tuple,dict,set,字符串)都可以用
for...in...
迭代默认情况下,dict 迭代的是 key。
- 如果要迭代 value,可以用
for value in d.itervalues()
- 如果要同时迭代 key 和 value,可以用
for k, v in d.iteritems()
。
- 如果要迭代 value,可以用
判断一个对象是否是可迭代对象:
1
2
3
4from collections import Iterable
isinstance('abc', Iterable) # str是否可迭代 True
isinstance([1,2,3], Iterable) # list是否可迭代 True
isinstance(123, Iterable) # 整数是否可迭代 False拥有下标的循环:
1
2for i, value in enumerate(['A', 'B', 'C']):
print i, valuefor
循环同时引用两个变量:1
2for x, y in [(1, 1), (2, 4), (3, 9)]:
print x, y
列表生成式(List Comprehensions)
[x * x for x in range(1, 11)]
→[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- 加上判断:
[x * x for x in range(1, 11) if x % 2 == 0]
→[4, 16, 36, 64, 100]
- 两层循环(可以用来生成全排列):
[m + n for m in 'ABC' for n in 'XYZ']
→['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
生成器(Generator)
生成器里面装了用来生成一个 list 的算法,这样就不必创建完整的 list,从而节省大量的空间,一边循环,一边计算。
1 | g = (x * x for x in range(3)) |
也可以用for
循环:
1 | g = (x * x for x in range(10)) |
定义 generator 的另一种方法,
yield
:1
2
3
4
5
6
7# print Fibonacci list
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1改写成 generator:
1
2
3
4
5
6
7# Fibonacci generator
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 11
26) fib(
<generator object fib at 0x104feaaa0>generator 的执行顺序:
变成 generator 的函数,在每次调用
next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22def odd():
print 'step 1'
yield 1
print 'step 2'
yield 3
print 'step 3'
yield 5
...
o = odd()
o.next()
step 1
1
o.next()
step 2
3
o.next()
step 3
5
o.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
函数式编程(Functional Programming)
Python 对函数式编程提供部分支持。由于 Python 允许使用变量,因此,Python 不是纯函数式编程语言。
高阶函数
map
第一个参数是函数,第二个参数是可迭代对象,返回一个 listreduce
第一个参数是函数,第二个参数是可迭代对象,函数的两个参数(result, item)filter
sorted
第一个参数是可迭代对象,第二个参数是比较函数(有默认值)
返回函数
- Python 也有闭包的概念
匿名函数(lambda)
lambda x: x * x
等同于:
1 | def f(x): |
装饰器(Decorator)
@
1 | def log(func): |
1 |
|
相当于执行了:
1 | now = log(now) |
但此时,now.__name__
已经被替换成了wrapper
,所以完整的写法:
1 | import functools |
偏函数(Partial function)
可以固定某个参数的默认值,比如下方的例子,固定了int
函数的base
参数。
1 | int2 = functools.partial(int, base=2) |
模块
模块:一个文件就是一个模块
包:就是不同的文件夹,但每个包目录下面都要有一个__init__.py
的文件,否则就是一个普通目录而不是一个包。__init.__.py
可以是空文件,也可以有代码,因为__init__.py
本身就是一个模块,它的模块名就是它的父目录。比如如下的文件结构:
文件www.py
的模块名就是mycompany.web.www
,两个文件utils.py
的模块名分别是mycompany.utils
和mycompany.web.utils
。
引入模块
1 | #!/usr/bin/env python |
第 1 行和第 2 行是标准注释,第 1 行注释可以让这个hello.py
文件直接在 Unix/Linux/Mac 上运行,第 2 行注释表示.py 文件本身使用标准 UTF-8 编码;
第 4 行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第 6 行使用__author__
变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
sys.argv
用 list 存储了命令行的所有参数。python hello.py
对应['hello.py']
,python hello.py Michael
对应 ['hello.py', 'Michael]
。
最后两行:当用命令行运行hello
模块时,Python 解释器把一个特殊变量__name__
置为__main__
,而如果在其他地方导入该hello
模块时,则不会。
别名:
cStringIO
是 C 写的,速度更快,这样就可以优先导入cStringIO
。如果有些平台不提供cStringIO
,还可以降级使用StringIO
。1
2
3
4try:
import cStringIO as StringIO
except ImportError: # 导入失败会捕获到ImportError
import StringIO还有类似
simplejson
这样的库,在 Python 2.6 之前是独立的第三方库,从 2.6 开始内置1
2
3
4try:
import json # python >= 2.6
except ImportError:
import simplejson as json # python <= 2.5作用域:
前缀
_
表示私有函数,不应该被外部引用。
安装第三方模块
pip
pip install
是全局安装
默认情况下,Python 解释器会搜索当前目录、所有已安装的内置模块和第三方模块。搜索路径存放在sys
模块的path
变量中,可以添加要搜索的目录:
1 | import sys |
__future__
模块
Python 提供了__future__
模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性,比如:
1 | # still running on Python 2.7 |
1 | from __future__ import division |
面向对象编程
1 | class Student(object): |
类和实例
1 | class Student(object): |
class
后面紧接着是类名,即Student
,类名通常是大写开头的单词,紧接着是(object)
,表示该类是从哪个类继承下来的。
__init__
函数是构造函数。
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。
访问限制
- 在 Python 中,实例的变量名如果以
__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。 - 变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是 private 变量,所以,不能用__name__
、__score__
这样的变量名。 - 以一个下划线开头的实例变量名,比如
_name
,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。 - 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问
__name
是因为 Python 解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量
继承与多态
OOP 的特性,不作多余解释。
获取对象信息
type()
1
2
3
4
5
6
7
8
9
10123) type(
<type 'int'>
'str') type(
<type 'str'>
None) type(
<type 'NoneType'>
type(abs)
<type 'builtin_function_or_method'>
type(a)
<class '__main__.Animal'>types
模块types.StringType
、types.UnicodeType
、types.ListType
、types.TypeType
(所有类型本身的类型就是TypeType
)isinstance()
1
2
3
4
5
6
7
8# object -> Animal -> Dog -> Husky
a = Animal()
d = Dog()
h = Husky()
isinstance(h, Husky) # True
isinstance(h, Dog) # True
isinstance(h, Animal) # True判断一个变量是否是某些类型中的一种
1
2isinstance('a', (str, unicode)) # True
isinstance(u'a', (str, unicode)) # Truedir()
:获得对象的所有属性和方法,返回一个 list类似
__xxx__
的属性和方法在 Python 中都是有特殊用途的。比如__len__
方法返回长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法:1
2
3
4
5
6class MyObject(object):
def __len__(self):
return 100
obj = MyObject()
len(obj) # 100hasattr
、setattr
、getattr
1
2
3hasattr(obj, 'x') # 有属性'x'吗?
setattr(obj, 'y', 19) # 设置一个属性'y'
getattr(obj, 'z') # 获取属性'z'
__slots__
给 class 绑定新的方法,
MethodType
:1
2
3
4def set_score(self, score):
self.score = score
Student.set_score = MethodType(set_score, None, Student)限制类能添加的属性,
__slots
:1
2class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称对子类不起作用。
@property
1 | class Student(object): |
1 | s = Student() |
不定义 setter 的话,就是一个只读属性
多重继承
1 | class Dog(Mammal, Runnable): |
定制类
__str__()
方法用于自动类型转换1
2
3
4
5class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name__iter__()
,配合next()
方法,可以对类进行for...in
操作。1
2
3
4
5
6
7
8
9
10
11
12class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def next(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration();
return self.a # 返回下一个值1
2for n in Fib():
print n__getitem__()
:可以类似 list 那样,用方括号[]
取出对应的元素。1
2
3
4
5
6class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a1
2
3
4
5
6f = Fib()
f[0] # 1
f[1] # 1
f[10] # 89
f[:10] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
f[:10:2] # 未对step做处理,[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]__getitem__()
也没有对负数进行处理。__getitem__()
也可以将对象处理成一个 dict:1
2
3
4
5
6
7
8
9
10class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __getitem__(self, n):
if n == 'a':
return self.a
else:
return self.b1
2f = Test('aa', 'bb')
print f['a'] # 'aa'与之对应的是
__setitem__()
方法,把对象视作 list 或 dict 来对集合赋值。还有一个
__delitem__()
方法,用于删除某个元素。__getattr__()
,用于处理对象没有的属性或方法:1
2
3
4
5
6class Test(object):
def __init__(self, name):
self.name = name
def __getattr__(self, attr):
print 'Test doesn\'t have attribute: %s' % attr1
2test = Test('aa')
test.age # Test doesn't have attribute: age同样也可以返回方法。
只有在没有找到属性的情况下,才调用
__getattr__
。__call__()
使得实例本身可以称为一个函数。1
2
3
4
5
6
7class Student(object):
def __init__(self, name):
self.name = name
def __call__(self, age):
self.age = age
print('My name is %s and my age is %s' % self.name, age)1
2student = Student('Jackie')
student(25) # My name is Jackie and my age is 25可以用
callbale()
来判断一个对象是否能被调用。
元类
type()
不仅可以用来查看某个对象的类型,也可以用来创建新的类:1
2
3
4
5
6
7
8def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
h = Hello()
h.hello() # Hello, world.
print(type(Hello)) # <type 'type'>
print(type(h)) # <class '__main__.Hello'>传入三个参数:
- 类名
- 继承的父类的 tuple
- 属性和方法的 dict
通过
type()
函数创建的类和直接写 class 是完全一样的metaclass
元类,暂时不学
错误、调试和测试
错误处理
try...except...finally...
1 | try: |
except
还会捕获异常的子类,所有的错误类型都继承自BaseException
,所以在使用except
时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。
1 | BaseException |
logging
Python 内置的logging
模块可以非常容易地记录错误信息。
1 | # err.py |
1 | $ python err.py |
自己编写错误
1 | # err.py |
继续向上抛出异常
1 | # err.py |
调试
断言assert
1 | # err.py |
assert
语句本身会抛出异常,AssertionError
可以用-O
参数关闭 assert:python -O err.py
日志logging
几个级别:debug
,info
,warning
,error
。
可以指定级别,当我们指定level=INFO
时,logging.debug
就不起作用了。
1 | import logging |
单元测试
unittest
模块
1 | # mydict_test.py |
测试类:从
unittest.TestCase
继承。以
test
开头的方法才会被认为是测试方法,才能被执行用断言来判断是否符合期望,
assertEquals()
assertTrue()
,以及抛出异常:1
2with self.assertRaises(AttributeError):
value = d.empty
运行单元测试
在单元测试文件
mydict_test.py
下加上两行代码:1
2if __name__ == '__main__':
unittest.main()这样就可以像正常脚本一样运行了
$ python mydict_test.py
用参数
-m unittest
:$ python -m unittest mydict_test.py
setUp()
和tearDown()
分别会在每调用一个测试方法的前后分别被执行。
文档测试
doctest
模块:
1 | # mydict.py |
运行测试:python mydict.py
IO 编程
文件读写
1 | f = open('/Users/michael/test.txt', 'r') |
由于文件读写时都有可能产生IOError
,一旦出错,后面的f.close()
就不会调用。最好用try...finally
来写:
1 | try: |
Python 引入了with
语句来帮助调用:
1 | with open('/path/to/file', 'r') as f: |
read(size)
可以读取 size 个字节的内容readline()
可以读取一行内容readlines()
一次读取所有内容并按行返回list
操作文件和目录
os
模块
1 | import os |
os
模块不提供拷贝文件的接口,而shutil
模块提供了copyfile()
的函数。
序列化(pickling)
序列化:把变量从内存中变成可存储或传输的过程。
cPickle
和pickle
1 | try: |
1 | d = dict(name='Bob', age=20, score=88) |
JSON
内置的json
模块
1 | import json |
定制 JSON 序列化
将某个 class 的实例序列化成 JSON:
1 | import json |
- 偷懒的方法:利用
__dict__
属性,它就是一个dict
,用来存储实例变量。
反序列化:
1 | def dict2student(d): |
进程和线程
多进程(multiprocessing)
Unix/Linux 操作系统提供了一个fork()
系统调用,子进程永远返回0
,而父进程返回子进程的 ID。子进程只需要调用getppid()
就可以拿到父进程的 ID。
1 | # multiprocessing.py |
multiprocessing
模块 - 跨平台
1 | from multiprocessing import Process |
1 | Parent process 928. |
Pool 进程池
1 | from multiprocessing import Pool |
Pool
默认大小是 CPU 核数,当创建多于Pool
大小的进程数时,后面的进程就会等待前面的进程执行完成再执行。
1 | Parent process 669. |
也可以设置p = Pool(5)
同时运行 5 个进程。
进程间通讯
Queue
策略
1 | from multiprocessing import Process, Queue |
还有Pipes
等策略
多线程
threading
高级模块对thread
模块进行了封装,较常使用。
1 | import time, threading |
1 | thread MainThread is running... |
Lock
1 | import time, threading |
多核 CPU
Python 因为存在 global interpret lock(GIL 锁)的关系,虽然可以多线程执行,但无法真正利用多核。每个进程最多利用 2 两个核。
ThreadLocal
为每个线程单独建立作用域
1 | import threading |